<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body></body>
<script>
  class MyPromise {
    constructor(executor) {
      this.value = null
      this.status = MyPromise.PENDING
      try {
        executor && executor(this.resolve.bind(this), this.reject.bind(this))
      } catch (err) {
        this.reject(err)
      }
      this.nextPromise = null
      this.prevPromise = null
      this.callBacks = null
    }
    resolve(value) {
      if (this.status !== MyPromise.PENDING) return
      this.status = MyPromise.FULFILLED
      this.value = value
      if (this.callBacks) this.callBacks.onFulfilled()
    }
    reject(value) {
      if (this.status !== MyPromise.PENDING) return
      this.status = MyPromise.REJECTED
      this.value = value
      if (this.callBacks) this.callBacks.onRejected()
    }
    then(onFulfilled = value => value, onRejected = value => value) {
      this.addCallBacks({ onFulfilled, onRejected })
      if (this.status !== MyPromise.PENDING) this.runCallBack()
      const nextPromise = new MyPromise()
      nextPromise.prevPromise = this
      return this.nextPromise = nextPromise
    }
    resultHander(fn) {
      let { nextPromise, value } = this
      try {
        let res = fn(value)
        if (res instanceof MyPromise) {
          res.then(val => nextPromise.resolve(val), err => nextPromise.reject(err))
        } else if (this.status == MyPromise.FULFILLED || (this.status == MyPromise.REJECTED && this.prevPromise)) {
          nextPromise.resolve(res)
        } else if (this.status == MyPromise.REJECTED && !this.prevPromise) {
          nextPromise.reject(res)
        }
      } catch (err) {
        nextPromise.reject(err)
      }
    }
    addCallBacks({ onFulfilled, onRejected }) {
      this.callBacks = {
        onFulfilled: () => setTimeout(this.resultHander.bind(this, onFulfilled)),
        onRejected: () => setTimeout(this.resultHander.bind(this, onRejected)),
      }
    }
    runCallBack() {
      if (this.status == MyPromise.FULFILLED) this.callBacks.onFulfilled()
      if (this.status == MyPromise.REJECTED) this.callBacks.onFulfilled()
    }

    static resolve(value) {
      if (value instanceof MyPromise) {
        return value
      }
      return new MyPromise().resolve(value)
    }
    static reject() {
      if (value instanceof MyPromise) {
        return value
      }
      return new MyPromise().reject(value)
    }

    static all(promises) {
      const p = new MyPromise()
      let values = []
      promises.forEach((item, index) => {
        item.then(value => {
          values[index] = value
          if (values.length === promises.length && (Array.from(values).every(arrItem => arrItem))) {
            p.resolve(values)
          }
        }, err => p.reject(err))
      })
      return p
    }

    static race(promises) {
      const p = new MyPromise()
      promises.forEach(item => item.then(value => p.resolve(value), err => p.reject(err)))
      return p
    }
  }

  MyPromise.PENDING = 'pending'
  MyPromise.FULFILLED = 'fulfilled'
  MyPromise.REJECTED = 'rejected'


  // 链式操作测试
  // let p = new MyPromise((resolve, reject) => {
  //   reject("reject")
  // }).then(value => {
  //   console.log(value)
  // }, (err) => {
  //   console.log(err)
  //   return err
  // }).then(res => {
  //   console.log(res)
  // }, err => {
  //   console.log(err)
  // })


  //all测试
  let p1 = new MyPromise((resolve, reject) => {
    setTimeout(() => resolve("拒绝"), 1000)
  })
  let p2 = new MyPromise((resolve, reject) => {
    setTimeout(() => resolve("成功"), 500)
  })

  MyPromise.all([p1, p2]).then(values => console.log(values), err => console.log(err))


  // race 测试
  // let p1 = new MyPromise((resolve, reject) => {
  //   setTimeout(() => { resolve(`成功`) }, 2000);
  // })
  // let p2 = new MyPromise((resolve, reject) => {
  //   setTimeout(() => { reject(`拒绝`) }, 1000);
  // })
  // MyPromise.race([p1, p2]).then(value => {
  //   console.log(value);
  // }, err => {
  //   console.log(err);
  // })



</script>

</html>